package my;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class RSAUtils {
    
    // 加密算法
    public static final String KEY_ALGORITHM = "RSA";
    
    // 签名算法
    public static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
    
    // RSA最大加密明文大小
    private static final int MAX_ENCRYPT_BLOCK = 117;
    
    // RSA最大解密密文大小
    private static final int MAX_DECRYPT_BLOCK = 128;
    
    /**
     * 生成RSA密钥对（2048位）
     */
    public static Map<String, String> generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGen.initialize(2048); // 推荐使用2048位[3](@ref)
        KeyPair keyPair = keyPairGen.generateKeyPair();
        
        // 获取公钥和私钥的Base64编码字符串
//        String publicKey = Base64.getEncoder().encodeToString(
//            keyPair.getPublic().getEncoded());
//        String privateKey = Base64.getEncoder().encodeToString(
//            keyPair.getPrivate().getEncoded());
        
        Map<String, String> keyMap = new HashMap<>();
        keyMap.put("publicKey", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA41QI93zyjfr5xhmv7Zc0fRyt5oA8pHKoKcjJkSCPhz1Cfiec43RVdNVpGGC5R6VLUd/HI+i4kS5H0xdK4kGyBaCgnrKa6LFTfbMAHZiW0eQ+SVEw8vmP5LVvAF0ojMFP3Znzr6sQbz3umU+h64NqAvDuMRiGZpqiHlJGdpJ6QEoExBg6Zw8+U7nRa6mVTaerx48k0+x+gTx9A1JxQw3jw0lDCrocrECiZHrd+mEepvckqpGCQdCGfzf3rAGWyd1Uv0jXPpVAUByV69sNsmvxur9JGgpzJK6AIz4xOVC8n5Hmsk184RaiMEyC1BgpXgK4G82L7X62z79lnuFRevilDwIDAQAB");
        keyMap.put("privateKey", "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDjVAj3fPKN+vnGGa/tlzR9HK3mgDykcqgpyMmRII+HPUJ+J5zjdFV01WkYYLlHpUtR38cj6LiRLkfTF0riQbIFoKCesprosVN9swAdmJbR5D5JUTDy+Y/ktW8AXSiMwU/dmfOvqxBvPe6ZT6Hrg2oC8O4xGIZmmqIeUkZ2knpASgTEGDpnDz5TudFrqZVNp6vHjyTT7H6BPH0DUnFDDePDSUMKuhysQKJket36YR6m9ySqkYJB0IZ/N/esAZbJ3VS/SNc+lUBQHJXr2w2ya/G6v0kaCnMkroAjPjE5ULyfkeayTXzhFqIwTILUGCleArgbzYvtfrbPv2We4VF6+KUPAgMBAAECggEAGoWNXkJnkgT76w7IJbIEX6xe5YuEh7MQx38zdQkVdbPB9DHu7eJ+rdjQIBX4x9ETDoRLGXdnQRjjLE+FXwxHFrz8wEcaJ5e4o+ukUc+hmO9GDQu56D8ReJ9qSeKUfod3J+V0NsSAcFoLYjuWKoH0dk9hmry/wWy9CUiUC/Ve6e0AeHnnrly6HnVg+obReZRL95xtOR5HcckNPy/wyRC4K4LPf0TXCdYSoDewFxbLIvlvYSOiMQvy4NLkyGyBc+beA9efVxjgrKheNgv1kY8d0DFRx7gBfDF63T4nD3MQsSJe3i7tcaDpFXdtN7Lq2hSOqK2rp9pKcvpSG7pZ9qwn8QKBgQD6r8LNmgZ+t/Bb4C5kQMHPB7m73/RTf5l1XBuytvLLCOxp0GX092bctXxY+oX0DTZMcj4/Sr/Q2sGbY+6lHfE+wNw41o1anX/gQ2LL0NmR8hyZQC9GV8NAWlJzExx+bAQDevwUS0BXv/RUIv5O9m9WenbBudwRK66By74+m4lFRQKBgQDoJYfXzI1Am1XEB16qTqnPwve773/iJfsvjiFABTEWCq+DpXVbx/cFBV27FGJbfc9/aF7vgZt1qrKSz/dK2lEi07bGOa9hMXjsFdCYNnVEJq5FvyUhAh9PoYM0BIR1xir7o5qtvVVc6HWOaU/fum3KUbV26mElocNxt8z4L+u0QwKBgQCWrNPavkxPGAHJRzq7GU0kQdF6QgbZtSUorB8d0Oan8Iogo5jUn2tdF1xRIusmoFZVpokRg709LG8YFf2XkyKwhD0ZkKPPpkD5K6eTN8m99hcIadLAZfm+kUtjTUZgGk5jRSWEkSJwW7jgZVQTY/3RrbP5htvmGio+a5bCjLv6PQKBgDQ+hgTV3xAkJTgZpH9VdrfwG8qqyq/9djsslGURBgv82aUZ7oB6TyhNHdwFaWFHvhPhTERAedkQUmHh9bhdy5a36NFRmBuag6k1+5zV3Kw6vEusCdIU5TEJmzpwrdLHh1LcbeodywE+u34SS0yBa0X1/43Ej5plflOwVqugNT57AoGBANqjS4qlHC5emTi+KDkb7T9MjqzcurLDgyKgUCEKem0rFklkz6pRw51qyQZa4WyuPWuFnQYQ+x6xKzjPHFgydq/eLu2JCUAtjdpSdQWZFBKeXrtGT10ddZ2H2UtE4/xPi111NHzXyVQPrXWxdXqfOHwMLKlZvhisl/UH1nuaAThR");
        return keyMap;
    }
    
    /**
     * 使用公钥加密
     */
    public static String encrypt(String data, String publicKey) throws Exception {
        byte[] dataBytes = data.getBytes("UTF-8");
        PublicKey key = getPublicKey(publicKey);
        
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // 使用PKCS1填充[3](@ref)
        cipher.init(Cipher.ENCRYPT_MODE, key);
        
        // 分段处理大数据[2](@ref)
        return Base64.getEncoder().encodeToString(processData(dataBytes, cipher, MAX_ENCRYPT_BLOCK));
    }
    
    /**
     * 使用私钥解密
     */
    public static String decrypt(String encryptedData, String privateKey) throws Exception {
        byte[] dataBytes = Base64.getDecoder().decode(encryptedData);
        PrivateKey key = getPrivateKey(privateKey);
        
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, key);
        
        byte[] decryptedData = processData(dataBytes, cipher, MAX_DECRYPT_BLOCK);
        return new String(decryptedData, "UTF-8");
    }
    
    /**
     * 数据分段处理（解决RSA加密数据长度限制问题）[2](@ref)
     */
    private static byte[] processData(byte[] data, Cipher cipher, int maxBlock) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        
        while (inputLen - offSet > 0) {
            int length = Math.min(inputLen - offSet, maxBlock);
            cache = cipher.doFinal(data, offSet, length);
            out.write(cache, 0, cache.length);
            offSet += maxBlock;
        }
        
        byte[] result = out.toByteArray();
        out.close();
        return result;
    }
    
    /**
     * 从字符串加载公钥
     */
    private static PublicKey getPublicKey(String publicKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePublic(keySpec);
    }
    
    /**
     * 从字符串加载私钥
     */
    private static PrivateKey getPrivateKey(String privateKey) throws Exception {
        byte[] keyBytes = Base64.getDecoder().decode(privateKey);
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        return keyFactory.generatePrivate(keySpec);
    }
    
    /**
     * 生成数字签名
     */
    public static String sign(String data, String privateKey) throws Exception {
        PrivateKey key = getPrivateKey(privateKey);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(key);
        signature.update(data.getBytes("UTF-8"));
        return Base64.getEncoder().encodeToString(signature.sign());
    }
    
    /**
     * 验证数字签名
     */
    public static boolean verify(String data, String publicKey, String sign) throws Exception {
        PublicKey key = getPublicKey(publicKey);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(key);
        signature.update(data.getBytes("UTF-8"));
        return signature.verify(Base64.getDecoder().decode(sign));
    }
}